# 导入flask相关的库
from urllib import request

import pandas as pd
from flask import Flask, render_template, redirect, url_for, session, request
from pyecharts.charts import * # 导入饼图模块
from pyecharts import options as opts  # 导入配置选项模块，并给它起一个别名 opts


from tools.actor import getAllActorMovieNum, getAllDirectorMovieNum
from tools.addressData import getAddressData, getLangData
from tools.getData import mainFun
from tools.homeData import *
from tools.rateData import getRate_tType, getStart, getMean, getCountryRating
from tools.timeData import getTimeList, getMovieTimeList
from tools.typeData import getMovieTypeData
from tools.word_cloud import getTitleImg, getCommentsImg, getCastsImg
from functools import wraps

# 导入工具文件
# 创建Flask对象，指定静态文件夹
app = Flask(__name__, static_folder='static', template_folder='templates')
app.secret_key = 'ywqq'

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'username' not in session:
            #如果没有登录，则重定向到登录界面
            return redirect(url_for('login',next=request.url))
        return f(*args, **kwargs)
    return decorated_function

# 实现一个试图函数，作为根路由
@app.route('/')
def rootRoute():
    return render_template('login.html')

# 实现注册
@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']  # 获取用户名和密码
        password = request.form['password']
        if not username or not password:  # 如果没有用户名和密码，则重定向回注册界面
            return redirect(url_for('register'))
        # 如果两次密码不一致
        if request.form['password'] != request.form['passwordCheked']:
            return '<b>两次密码不一致！！！</b>'
        conn, cursor = get_conn()  # 调用数据库连接，返回两个参数
        cursor.execute('select username from users')
        data = cursor.fetchall()  # 查询数据库中用户的所有信息
        usernames = [user[0] for user in data]
        if username in usernames:  # 判断是否有相同的用户名
            return '<b>用户名已经存在！！！</b>'
        # 如果填写的信息完全正确，就将信息写入到数据库中
        cursor.execute('insert into users (username, password) values (%s, %s)', (username, password))
        conn.commit()  # 提交事务
        return redirect(url_for('login'))
    return render_template('register.html')


# 实现登录
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        if not username or not password:
            return redirect(url_for('login'))
        conn, cursor = get_conn()  # 调用连接
        cursor.execute('select * from users where username = "%s" and password = "%s"' % (username, password))
        user = cursor.fetchone()  #找到一个
        if user:  #如果用户存在，保存用户信息到Session中
            session['username'] = username
            return redirect(url_for('home'))  #重定向到首页
        else:  # 如果没有用户名则跳转回登录页面
            return redirect(url_for('login'))
    return render_template('login.html')


#实现首页
@app.route('/home')
@login_required
def home():
    username = session['username']  # 根据Session获取值
    # 获取首页需要显示的所有信息，并从后台--前端进行展示出来
    allData = getAllData()  # 获取所有的电影数据
    dataLen = len(allData)  # 统计长度
    maxRate = getMaxRate()  # 豆瓣最高评分
    maxCast = getMaxCast()  # 出场最多演员
    typeAll = getTypesAll()  # 电影种类数
    typeAll = len(typeAll)  # 统计长度
    maxLang = getMaxLang()  # 获取最多的语言
    # 获取图表需要的数据，传递到前端页面，通过ECharts进行可视化
    types = getType_t()  # 电影种类饼状图数据
    x, y = getRate_t()  # 电影评分折线图数据
    return render_template('home.html', username=username, dataLen=dataLen,
                           maxRate=maxRate, maxCast=maxCast, typeAll=typeAll,
                           maxLang=maxLang, types=types,x=list(x), y=list(y))


#实现搜素
@app.route('/search/<int:searchId>',methods=['GET', 'POST'])
def search(searchId):
    username = session['username']
    allData = getAllData()
    data=[]
    if request.method == 'GET':
        if searchId ==0:
            return render_template('search.html',username=username)
        #遍历所有的电影，找到和迪纳基电影名字一样的电影
        for i in allData:
            if i[0] == searchId:
                data.append(i)
        return render_template('search.html', username=username)
    else:
        searchWord = request.form['searchIpt']
        if not searchWord: #搜索为空，重定向到搜索界面
            return redirect(url_for('search',searchId=searchId))
        #过滤函数
        def filterFun(item):
            if item[3].find(searchWord) ==-1: #找不到输入的内容
                return False
            else:
                return True
        data = list(filter(filterFun,allData))# 将找到的数据装维列表
        return render_template('search.html',username=username,data=data)

#实现时间分析
@app.route('/time_t')
def time_t():
    username = session['username']
    x, y = getTimeList()  # 历年产量统计
    movieTimeData = getMovieTimeList()  # 电影数据时长分布占比
    return render_template('time_t.html', username=username,
                           x=list(x), y=list(y), movieTimeData=movieTimeData)


# 实现评分分析表
@app.route('/rate_t/<type>', methods=['GET', 'POST'])
def rate_t(type):
    username = session['username']
    # 电影评分统计
    typeAll = getTypesAll()  # 获取电影种类
    if type == 'all':
        x, y = getRate_t()  # 获取所有的电影种类的评分
    else:
        x, y = getRate_tType(type)  # 获取不同类型的电影评分
    # 豆瓣评分星级饼状图
    if request.method == 'GET':
        star, movieName = getStart("")  # 不写则绘制数据库中第一条电影的数据
    else:
        searchWord = request.form['searchIpt'].strip()
        if not searchWord or searchWord == '':
            return redirect(url_for('rate_t', type=type))
        # 如果没有问题就获取输入电源的星级占比和名称
        try:
            star, movieName = getStart(searchWord)
        except Exception as e:
            return redirect(url_for('rate_t', type=type))
    # 豆瓣年度评价评分柱状图的数据
    x1, y1 = getMean()
    # 豆瓣电影中外评分分布图
    x2, y2, y22 = getCountryRating()
    return render_template('rate_t.html', username=username,
                           x=list(x), y=list(y), star=star, movieName=movieName,
                           x1=x1, y1=y1, x2=x2, y2=y2, y22=y22, typeAll=typeAll)

# 实现地图分析表
@app.route('/address_t')
def address_t():
    username = session['username']
    x, y = getAddressData()  # 电影拍摄地点统计
    x1, y1 = getLangData()  # 电影语言统计
    return render_template('address_t.html', username=username, x=x, y=y, x1=x1, y1=y1)

# 类型分析表
@app.route('/type_t')
def type_t():
    username = session['username']
    result = getMovieTypeData()
    result = sorted(result, key=lambda x: x['value'], reverse=True)
    return render_template('type_t.html', username=username, result=result[:10])

# 实现导演和演员
@app.route('/actor_t')
def actor_t():
    username = session['username']
    x, y = getAllActorMovieNum()  # 导演作品数量前20
    x1, y1 = getAllDirectorMovieNum()  # 演员参演排名前20
    return render_template('actor_t.html', username=username,
                           x=x, y=y, x1=x1, y1=y1)

# 实现数据操作
@app.route('/tables/<int:id>')
def tables(id):
    username = session['username']
    tablelist=[]#存储数据
    if id == 0:
        tablelist=getTableList()
    return render_template('tables.html',username=username, tablelist=tablelist)


# 实现标题词云
@app.route('/title_c')
@login_required
def title_c():
    username = session['username']
    # 调用词云函工具文件中的函数，进行绘制词云
    getTitleImg('title','fab fa-apple','./static/images/title.png')
    return render_template('title_c.html', username=username)

# 演员名词云
@app.route('/casts_c')
@login_required
def casts_c():
    username = session['username']
    # 调用词云函工具文件中的函数，进行绘制词云
    getCastsImg('casts','fas fa-sun','./static/images/casts.png')
    return render_template('casts_c.html', username=username)

#自定义词云
@app.route('/comments_c', methods=['GET', 'POST'])
@login_required
def comments_c():
    username = session['username']
    if request.method == 'GET':
        return render_template('comments_c.html', username=username)
    else:
        searchWord = request.form['searchIpt']
        try:
            getCommentsImg('commentContent',searchWord,'fab fa-qq', './static/images/comments.png')
        except Exception as e:
            return redirect(url_for('comments_c'))
    return render_template('comments_c.html', username=username)



# 统计电影类型TOP5数量（饼图）
def typeData():
    # 读取数据
    data = pd.read_csv('./tools/data/type_counts.csv', encoding='utf-8')
    pie_data = list(zip(data['类型'], data['数量']))
    pie = Pie(init_opts=opts.InitOpts(height='400px', width='500px'))
    pie.add('数量',
            pie_data,
            radius=['30%', '60%'],
            center=['50%', '50%'],
            label_opts=opts.LabelOpts(formatter='{b}:{c}个', font_size=16, font_style='bold', color='#00ff00'), )
    pie.set_global_opts(
        title_opts=opts.TitleOpts(title=''),
        legend_opts=opts.LegendOpts(textstyle_opts=opts.TextStyleOpts(font_size=16, color='#FFFFFF'))
    )
    pie.render('static/html/type_data.html')
    chart_html1 = pie.render_embed()
    return chart_html1

# 统计年份电影TOP5数量
def yearData():
    # 读取数据
    data = pd.read_csv('./tools/data/year_counts.csv', encoding='utf-8')
    years = data['年份'].tolist()
    counts = data['数量'].tolist()
    bar = Bar(init_opts=opts.InitOpts(height='400px', width='500px'))
    bar.add_xaxis(years)
    bar.add_yaxis('数量', counts)
    bar.set_global_opts(
        title_opts=opts.TitleOpts(title=''),
        xaxis_opts=opts.AxisOpts(name='年份',
                                 name_textstyle_opts=opts.TextStyleOpts(color='#00ff00'),
                                 axislabel_opts=opts.LabelOpts(font_size=12, color='#ccff66')),
        yaxis_opts=opts.AxisOpts(name='数量',
                                 name_textstyle_opts=opts.TextStyleOpts(color='#00ff00'),
                                 axislabel_opts=opts.LabelOpts(font_size=12, color='#ccff66')),
        legend_opts=opts.LegendOpts(textstyle_opts=opts.TextStyleOpts(font_size=15, color='#FFFFFF')),
        tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross')
    )
    bar.set_series_opts(itemstyle_opts=opts.ItemStyleOpts('#00ffff'))
    bar.render('static/html/year_data.html')
    chart_html2 = bar.render_embed()
    return chart_html2

# 电影中英文占比
def langData():
    # 英语,99/99+94
    # 汉语普通话,94/99+94
    data = pd.read_csv('./tools/data/lang_counts.csv', encoding='utf-8')
    # 将DataFrame格式转换为列表格式  [['英语', 99], ['汉语普通话', 94]]
    # result = [[row['语言'], row['数量']] for index, row in data.iterrows()]
    result = data.values.tolist()
    # 计算中英文语言的总数，用于计算百分比 193
    max_value = result[0][1] + result[1][1]
    # 中文水球图
    li1 = Liquid(init_opts=opts.InitOpts(height='100px', width='100px'))
    li1.add(
        '占比',
        [result[1][1] / max_value],  # 计算中文占比
        center=['15%', '35%'],  # 中心点
        shape='pin',
        is_outline_show=False,
        color=['#0f0'],
        label_opts=opts.LabelOpts(font_size=40)
    )
    li1.set_global_opts(title_opts=opts.TitleOpts(title=result[1][0][:2], pos_left='13%', pos_top='5%',
                                                  title_textstyle_opts=opts.TextStyleOpts(color='#0f0')), )

    # 英文水球图
    li2 = Liquid(init_opts=opts.InitOpts(height='100px', width='100px'))
    li2.add(
        '占比',
        [result[0][1] / max_value],  # 计算英文占比
        center=['35%', '35%'],  # 中心点
        shape='pin',
        is_outline_show=False,
        color=['#00f'],
        label_opts=opts.LabelOpts(font_size=40)
    )
    li2.set_global_opts(title_opts=opts.TitleOpts(title=result[0][0][:2], pos_left='32%', pos_top='5%',
                                                  title_textstyle_opts=opts.TextStyleOpts(color='#00f')), )

    # 组合
    grid = Grid()
    grid.add(li1, grid_opts=opts.GridOpts())
    grid.add(li2, grid_opts=opts.GridOpts())
    grid.render('static/html/lang_data.html')
    chart_html3 = grid.render_embed()
    return chart_html3

# 电影评论数柱状图
def commentData():
    data = pd.read_csv('./tools/data/comment_counts.csv', encoding='utf-8')
    names = data['电影'].tolist()
    counts = data['数量'].tolist()
    bar = Bar(init_opts=opts.InitOpts(height='400px', width='500px'))
    bar.add_xaxis(names)
    bar.add_yaxis('数量', counts)
    bar.set_global_opts(
        title_opts=opts.TitleOpts(title=''),
        xaxis_opts=opts.AxisOpts(name='电影',
                                 name_textstyle_opts=opts.TextStyleOpts(color='#00ff00'),
                                 axislabel_opts=opts.LabelOpts(font_size=12, rotate=45, color='#ccff66')),
        yaxis_opts=opts.AxisOpts(name='数量',
                                 min_=500000,
                                 max_=3500000,
                                 name_textstyle_opts=opts.TextStyleOpts(color='#00ff00'),
                                 axislabel_opts=opts.LabelOpts(font_size=10, rotate=45, color='#ccff66')),
        legend_opts=opts.LegendOpts(textstyle_opts=opts.TextStyleOpts(font_size=15, color='#FFFFFF')),
        tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross')
    )
    bar.set_series_opts(itemstyle_opts=opts.ItemStyleOpts('#ccff66'))
    bar.render('static/html/comment_data.html')
    chart_html4 = bar.render_embed()
    return chart_html4

# 地球
def worldData():
    data = pd.read_csv('./tools/data/country_counts.csv', encoding='utf-8')
    # 将DataFrame转为列表
    # [['中国大陆', 92], ['美国', 78], ['中国香港', 26], ['英国', 24], ['日本', 10],
    # ['法国', 9], ['加拿大', 7], ['韩国', 4], ['意大利', 4], ['印度', 4]]
    data_list = data.values.tolist()
    # 提供国家地区数据，用替换原数据中国家的名称（只是为了替换一些面积大一些的国家，在地图好看一些）
    country_name = ['Russia', 'Canada', 'China', 'USA', 'Brazil', 'Australia', 'India', 'Argentina', 'France',
                    'Kazakhstan']
    # 初始化列表，存储映射后的结果
    replace_data = []
    # [['Russia', 92], ['Canada', 78], ['China', 26], ['USA', 24], ['Brazil', 10],
    # ['Australia', 9], ['India', 7], ['Argentina', 4], ['France', 4], ['Kazakhstan', 4]]
    for i in range(len(country_name)):
        replace_data.append([country_name[i], data_list[i][1]])

    # 计算最大值最小值
    low = min([x[1] for x in replace_data])
    high = max([x[1] for x in replace_data])
    # 绘制
    map = MapGlobe(init_opts=opts.InitOpts(height='500px', width='450px'))
    map.add(maptype='world', series_name='数量', data_pair=replace_data)
    map.set_global_opts(
        tooltip_opts=opts.TooltipOpts(trigger='item'),
        visualmap_opts=opts.VisualMapOpts(
            min_=low,
            max_=high,
            type_='color',
            range_color=['#f00', '#00f'],
        )
    )
    map.render('static/html/map3d.html')


# 实现大屏
@app.route('/analysis1', methods=['GET', 'POST'])
def index():
    # 获取首页需要显示的所有信息，并从后台--前端进行展示出来
    allData = getAllData()  # 获取所有的电影数据
    allData = len(allData)  # 统计长度
    maxRate = getMaxRate()  # 豆瓣最高评分
    typeAll = getTypesAll()  # 电影种类数
    typeAll = len(typeAll)  # 统计长度
    maxLang = getMaxLang()  # 获取最多的语言
    data = {
        'allData': allData,
        'maxRate': maxRate,
        'typeAll': typeAll,
        'maxLang': maxLang
    }
    chart_html1 = typeData()
    chart_html2 = yearData()
    chart_html3 = langData()
    chart_html4 = commentData()
    worldData()
    return render_template('analysis.html', data=data,
                           chart_html1=chart_html1,chart_html2=chart_html2,
                           chart_html3=chart_html3,chart_html4=chart_html4)

#退出登录
@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('login'))

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html')


# 程序入口
if __name__ == '__main__':
    with app.app_context():
        mainFun()
    app.run(debug=True, port=8687)
